<template>
  <view class="lsd-seamless-scroll" :style="{ height: height + 'px' }" @mouseenter="handleMouseEnter" @mouseleave="handleMouseLeave">
    <view 
      class="scroll-container" 
      :style="{ transform: `translateY(${containerPosition}px)` }"
      :class="{ 'scroll-transition': isTransitioning }"
    >
  <view 
        class="scroll-item" 
        :style="{ top: `${positionA}px` }"
        ref="itemA"
      >
        <view v-for="(item, index) in list" :key="`a-${index}`" class="item-wrapper">
          <slot name="item" :item="item" :index="index">
            <view class="default-item">{{ item }}</view>
          </slot>
        </view>
      </view>
      <view 
        class="scroll-item" 
        :style="{ top: `${positionB}px` }"
        ref="itemB"
      >
        <view v-for="(item, index) in list" :key="`b-${index}`" class="item-wrapper">
          <slot name="item" :item="item" :index="index">
            <view class="default-item">{{ item }}</view>
          </slot>
        </view>
      </view>
    </view>
  </view>
</template>

<script>
export default {
  name: 'lsd-seamless-scroll',
  props: {
    list: {
      type: Array,
      default: () => []
    },
    speed: {
      type: Number,
      default: 50
    },
    direction: {
      type: String,
      default: 'up',
      validator: value => ['up', 'down'].includes(value)
    },
    autoPlay: {
      type: Boolean,
      default: true
    },
    hoverStop: {
      type: Boolean,
      default: true
    },
    height: {
      type: Number,
      default: 200
    },
    itemHeight: {
      type: Number,
      default: 40
    },
    duplicateGap: {
      type: Number,
      default: 0
    }
  },
  data() {
    return {
      containerPosition: 0,
      positionA: 0,
      positionB: 0,
      isTransitioning: false,
      animationId: null,
      lastTime: 0,
      isPlaying: false,
      itemTotalHeight: 0,
      actualItemHeight: 0,
      raf: function(fn) {
        if (typeof requestAnimationFrame === 'function') {
          return requestAnimationFrame(fn)
        }
        return setTimeout(fn, 16)
      },
      caf: function(id) {
        if (typeof cancelAnimationFrame === 'function') {
          return cancelAnimationFrame(id)
        }
        return clearTimeout(id)
      }
    }
  },
  computed: {
    scrollDirection() {
      return this.direction === 'up' ? -1 : 1
    }
  },
  watch: {
    list: {
      handler(newVal) {
        if (newVal.length > 0) {
          this.calculateHeights()
          if (this.autoPlay && !this.isPlaying) {
            this.play()
          }
        }
      },
      immediate: true
    }
  },
  mounted() {
    this.$nextTick(() => {
      this.calculateHeights()
      if (this.autoPlay) {
        this.play()
      }
    })
  },
  beforeDestroy() {
    this.stop()
  },
  methods: {
    calculateHeights() {
      if (this.list.length === 0) {
        this.itemTotalHeight = 0
        this.positionA = 0
        this.positionB = 0
        return
      }
      const base = this.itemHeight
      this.actualItemHeight = base
      this.$nextTick(() => {
        try {
          const q = uni.createSelectorQuery().in(this)
          q.select('.item-wrapper').boundingClientRect(rect => {
            const rowH = rect && rect.height ? rect.height : base
            this.actualItemHeight = rowH
            const total = this.list.length * rowH
            this.itemTotalHeight = total
            this.positionA = 0
            this.positionB = total + this.duplicateGap
            if (this.autoPlay && !this.isPlaying) {
              this.play()
            }
          }).exec()
        } catch (e) {
          const total = this.list.length * base
          this.itemTotalHeight = total
          this.positionA = 0
          this.positionB = total + this.duplicateGap
          if (this.autoPlay && !this.isPlaying) {
            this.play()
          }
        }
      })
    },
    
    handleMouseEnter() {
      if (this.hoverStop && this.isPlaying) {
        this.pause()
      }
    },
    
    handleMouseLeave() {
      if (this.hoverStop && !this.isPlaying && this.autoPlay) {
        this.play()
      }
    },
    
    play() {
      if (this.isPlaying || this.list.length === 0) return
      
      this.isPlaying = true
      this.lastTime = Date.now()
      this.animate()
      this.$emit('scroll-start')
    },
    
    pause() {
      this.isPlaying = false
      if (this.animationId) {
        this.caf(this.animationId)
        this.animationId = null
      }
      this.$emit('scroll-pause')
    },
    
    stop() {
      this.pause()
      this.containerPosition = 0
      this.positionA = 0
      this.positionB = this.itemTotalHeight + this.duplicateGap
      this.$emit('scroll-stop')
    },
    
    animate() {
      if (!this.isPlaying) return
      
      const now = Date.now()
      const deltaTime = now - this.lastTime
      this.lastTime = now
      
      const distance = (this.speed * deltaTime) / 1000
      this.containerPosition += distance * this.scrollDirection
      
      this.checkContainerPosition()
      
      this.animationId = this.raf(this.animate.bind(this))
    },
    
    checkContainerPosition() {
      const h = this.itemTotalHeight
      const viewH = this.height
      const g = this.duplicateGap
      if (h <= 0 || viewH <= 0) return
      const yA = this.containerPosition + this.positionA
      const yB = this.containerPosition + this.positionB
      if (this.scrollDirection < 0) {
        if (yA + h <= 0) {
          this.positionA = this.positionB + h + g
        }
        if (yB + h <= 0) {
          this.positionB = this.positionA + h + g
        }
      } else {
        if (yA >= viewH) {
          this.positionA = this.positionB - h - g
        }
        if (yB >= viewH) {
          this.positionB = this.positionA - h - g
        }
      }
    },
  
    updateList(newList) {
      this.$emit('update:list', newList)
      this.calculateHeights()
      if (this.autoPlay && !this.isPlaying) {
        this.play()
      }
    }
  }
}
</script>

<style scoped>
.lsd-seamless-scroll {
  position: relative;
  overflow: hidden;
  width: 100%;
}

.scroll-container {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  will-change: transform;
}

.scroll-transition {
  transition: transform 0.3s ease;
}

.scroll-item {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  will-change: top;
}

.default-item {
  height: 40px;
  line-height: 40px;
  padding: 0 16px;
  border-bottom: 1px solid #eee;
  box-sizing: border-box;
}
</style>
